package convertTestsTojUnit;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;

import model.TestClusterBuilder;

import operations.AssignConstant;
import operations.CreateArray;
import operations.CreateObject;
import operations.Operation;

import ga.GAInfo;
import ga.Individual;
import instrumentation.TrackerCoverage;


public class JUnitTestGenerator  extends TrackerCoverage{
	/** maximum number of operation for each jUnit test */
	private static final int MAX_TEST_LEN = 2000;
	private final  Map<Integer,Individual> branchCoveredByIndividual;
	private final GAInfo     gaInfo;
	private final File destDir;
	private final TestSuite suite = new TestSuite();
	public JUnitTestGenerator(File destDir,GAInfo     gaInfo,Map<Integer,Individual> branchCoveredByIndividual) {
		this.destDir = destDir;
		this.branchCoveredByIndividual=branchCoveredByIndividual;
		this.gaInfo=gaInfo;
	}
	public void writeSuite() {
		suite.write(this.branchCoveredByIndividual,this.gaInfo);
	}


	private class TestSuite {
		
		private static final long serialVersionUID = -209417855781416430L;


		public void write(Map<Integer,Individual> branchCoveredByIndividual, GAInfo gaInfo) {
			List<String> testcases= new LinkedList<String>();
			HashSet<Individual> suiteBR= new HashSet<Individual>();
			for (Integer i : branchCoveredByIndividual.keySet()) {
				Individual test=	branchCoveredByIndividual.get(i);

				if(testcases.contains(test.toString()))continue;
				testcases.add(test.toString());
				suiteBR.add(test);
			}

				TestCase test = new TestCase(suiteBR,gaInfo);
				List<String> tests = test.write(gaInfo);

	}
	}
	private class TestCase{
		private final GAInfo     gaInfo;
		HashSet<Individual> suiteBR;
		private  String packageName;
		public TestCase(HashSet<Individual> suiteBR,GAInfo     gaInfo) {
			this.suiteBR=suiteBR;
			this.gaInfo=gaInfo;		
			{
				packageName=gaInfo.getTestCluster().getClassToTest().keySet().iterator().next().toString();
			}
		}
		private String getTestName(Integer idx) {
			String testName = "Stack".replace('-', '_');
			testName = testName.replace(' ', '_');
			if(idx != null) testName = testName + "_" + idx;
			return testName + "_TestCase";
		}
	
		public List<String> write( GAInfo     gaInfo) {
			
			final List<String> fileNames = new ArrayList<String>();
			int testLength = 0;
			int currentJUnitTest = 0;
			int currentTestMethod = 0;
			PrintWriter writer = null;
			for(Individual individual: suiteBR){//for (Entry<Integer, Individual> e : tests.entrySet()) {
				try {
					testLength += individual.getSize();
					if(writer == null || testLength > MAX_TEST_LEN) {
						if(writer != null) writeFooterAndClose(writer);
						
						String testName=getTestName(currentJUnitTest++);
						
						File dir = new File(destDir, packageName.replace('.', File.separatorChar));
						dir.mkdirs();

						File testFile = new File(dir, testName + ".java");
						writer = new PrintWriter(testFile);
						
						writeHeader(writer, testName);
					}
					writeTest( individual, writer, ++currentTestMethod);

					} catch (FileNotFoundException exc) {
						System.err.println( "JUnitTestGerenrator"+ "Cannot open the file: " + exc.getMessage()+" "+ exc);
					}
			}
			if(writer != null) writeFooterAndClose(writer);

			return fileNames;

		}
		private void writeHeader(PrintWriter out, String testName) {
			if(!packageName.isEmpty()) {
				out.println("package " + packageName + ";");
				out.println();
			}

			out.println("/** Test Generated by MAMDR */");
			out.println("public class " + testName + " extends junit.framework.TestCase {");
		}

		private void writeFooterAndClose(PrintWriter writer) {
			writer.println("}");
			writer.println();
			writer.close();
		}
		private void writeTest(Individual test, PrintWriter out, int testNumber) {
			Map<String, String[]> refFactory = this.gaInfo.getTestCluster().getRefFactory();
			out.println();

			out.println("\tpublic void test" + testNumber + "() throws Exception {");
			out.println();
			{	// create variables
//
//				// group references by type
				Map<String, List<String>> map = new HashMap<String, List<String>>();
				for(String ref:refFactory.keySet()){
					List<String> vars = map.get(ref);
					if(vars == null) {
						vars = new ArrayList<String>();
						map.put(ref, vars);
					}
					String[] refrences = refFactory.get(ref);
					for (int i = 0; i < refrences.length; i++) 
						vars.add(refrences[i]);
				}
				for (String type : map.keySet()) {
					out.print("\t\t" + type);

					boolean first = true;
					for (String var : map.get(type)) {
						if(first) first = false;
						else out.print(",");

						out.print(" " + var + " = null");
					}
					out.println(";");
				}
			}

			final List<Operation> initialCons  = test.getInitialConstructors();
			for(Operation op : initialCons ) {
					if( op instanceof CreateObject) {
						out.println("\t\ttry {");
						out.println("\t\t\t" + op + ";");
						out.println("\t\t} catch(Throwable e) {");
						out.println("\t\t\te.printStackTrace();");
						out.println("\t\t}");
				} else {
						out.println("\t\t" + op + ";");
				}
			}
			final List<Operation> ops  = test.getStatements();
			for(Operation op : ops ) {
				if( op instanceof CreateObject) {
					out.println("\t\ttry {");
					out.println("\t\t\t" + op + ";");
					out.println("\t\t} catch(Throwable e) {");
					out.println("\t\t\te.printStackTrace();");
					out.println("\t\t}");
			} else {
					out.println("\t\t" + op + ";");
			}
				
			}
			
			
			out.println("\t}");
		}
	}
}
